package cn.amossun.starter.mybatis.data.security.runner;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import cn.amossun.starter.common.constant.Constant;
import cn.amossun.starter.mybatis.data.security.config.MybatisPlusDataSecurityConfig;
import cn.amossun.starter.mybatis.data.security.loader.OrmConfigCacheLoader;
import cn.amossun.starter.redis.cache.exception.RedisCacheConfigException;
import cn.amossun.starter.redis.cache.properties.RedisProperties;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.LoadingCache;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import java.util.Optional;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

/**
 * @description: ORM配置缓存执行器
 * @author: Amos.Sun
 * @DateTime: 2020/05/30 11:34
 **/
@Slf4j
@Data
public class OrmConfigCacheRunner implements RedisCacheRunner {

    public final static String ORM_CACHE_KEY = "data-security" + Constant.COLON + "orm-cache-key";

    private final LoadingCache<String, Object> loadingCache;

    private final String ormCacheKey;

    public OrmConfigCacheRunner(RedisProperties redisProperties, MybatisPlusDataSecurityConfig mybatisConfig, OrmConfigCacheLoader ormConfigCacheLoader) {
        this.loadingCache = CacheBuilder.newBuilder()
                .maximumSize(mybatisConfig.getMybatisPlus().getCache().getMaximumSize())
                .refreshAfterWrite(mybatisConfig.getMybatisPlus().getCache().getDuration(), TimeUnit.SECONDS)
                .build(ormConfigCacheLoader);
        this.ormCacheKey = getOrmCacheKey(redisProperties);
    }

    protected String getOrmCacheKey(RedisProperties redisProperties) {
        if(ObjectUtil.isNull(redisProperties) || StrUtil.isEmpty(redisProperties.getPrefix())) {
            return ORM_CACHE_KEY;
        }
        return new StringBuilder(redisProperties.getPrefix().trim()).append(Constant.COLON).append(ORM_CACHE_KEY).toString();
    }

    @Override
    public void initCache() throws Exception {
        log.info("OrmConfigCacheRunner加载数据中.");
        Object object = loadingCache.get(getCacheKey());
        if(object != null && object instanceof Optional) {
            Optional<Object> optionalObject = (Optional<Object>)object;
            log.info("OrmConfigCacheRunner加载数据至GuavaCache: {}", optionalObject.isPresent() ? JSONUtil.toJsonStr(optionalObject.get()) : null);
        }
    }

    @Override
    public LoadingCache<String, Object> getLoadingCache() {
        return this.loadingCache;
    }

    @Override
    public String getCacheKey() throws RedisCacheConfigException {
        if(StrUtil.isEmpty(this.ormCacheKey)) {
            throw new RedisCacheConfigException("key为空值.");
        }
        return this.ormCacheKey;
    }

    @Override
    public Object getCacheValue() throws RedisCacheConfigException, ExecutionException {
        String cacheKey = getCacheKey();
        return getLoadingCache().get(cacheKey);
    }

    @Override
    public boolean refresh() throws RedisCacheConfigException {
        String cacheKey = getCacheKey();
        getLoadingCache().refresh(cacheKey);  //主动刷新本地Redis缓存
        return true;
    }

}
